package com.pijunn.label

import grails.gorm.DetachedCriteria
import java.sql.ResultSet;

import org.springframework.dao.DataIntegrityViolationException
import org.springframework.validation.FieldError;

import com.pijunn.common.enums.*
import com.pijunn.users.AppUser

class LabelController {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

	def	springSecurityService
	def messageSource
	// Export service provided by Export plugin
	def exportService
	def grailsApplication  //inject GrailsApplication

    def index() {
        redirect(action: "list", params: params)
    }

    def list(Integer max) {
		def items
		def itemCount
		def criteria
		
        params.max = Math.min(max ?: 10, 100)
		if (params.assigned || params.startSerial || params.endSerial || params.type) {
			criteria = new DetachedCriteria(Label).build {
				if (params.assigned)
					isNotNull ("registeredTo")
				if (params.startSerial && params.endSerial)
					between ('serialNumber', params.int('startSerial'), params.int('endSerial') )
				if (params.type)
					eq ('type', LabelType.valueOf(params.type))
			}
			itemCount = criteria.count()
			items = criteria.list(params)
		}
		else {
			items = Label.list (params)
			itemCount = Label.count()
		}
		
		// For export
		if(params?.format && params.format != "html"){
			// Make sure list is filtered
			if (!params.assigned && !params.startSerial && !params.endSerial && !params.type) {
				flash.message = 'Please filter the list before exporting'
				return [labelInstanceList: items, labelInstanceTotal: itemCount, params: params]
			}
			
			response.contentType = grailsApplication.config.grails.mime.types[params.format]
			response.setHeader("Content-disposition", "attachment; filename=labels.${params.extension}")
			
			List fields = ["serialNumber", "registrationDate", "type", "assigned", "registeredTo", "itemName", "itemDescription"]
			Map labels = ["serialNumber": "Label Serial", "registrationDate": "Date Registered", "type": "Label Type", "assigned": "Assigned", "registeredTo": "Registered To", "itemName": "Short Name", "itemDescription": "Description"]
			
			// Formatter closure
			def itemName = { domain, value ->
				return domain?.item?.collect { return it.name}.join('')
			}
			def itemDesc = { domain, value ->
				return domain?.item?.collect { return it.description}.join('')
			}

			Map formatters = [itemName: itemName, itemDescription: itemDesc] 
			Map parameters = [title: "Pijunn Labels", "column.widths": [0.1, 0.1, 0.1, 0.2, 0.1, 0.1, 0.1]]
			
			exportService.export(params.format, response.outputStream, criteria.list(), fields, labels, formatters, parameters)
		 }


        [labelInstanceList: items, labelInstanceTotal: itemCount, params: params]
    }

    def create = { GenerateLabelsCommand cmd ->
		if (request.method == 'POST') {
			if (cmd.validate()) {
				def user = springSecurityService.currentUser
				Integer currentLabelSerial = params.int('startSerialNumber'),
						end = params.int('endSerialNumber')
				
				while(currentLabelSerial <= end) {
					def label = new Label(serialNumber: currentLabelSerial, creationDate: new Date(), createdByUserName: user.username, type: params.type)
					if (label.validate()) {
						log.debugEnabled ? log.debug('Generating label with id: ' + currentLabelSerial) : ''
						label.save(flush:true)
					}
					else if(!cmd.errors.hasErrors()){
						def labelMessage
						label.errors.allErrors.each {FieldError error ->
				            final String field = error.field?.replace('label.', '')
				            final String code = "generateLabelsCommand.$field.$error.code"
							log.debug(error)
				            cmd.errors.rejectValue("startSerialNumber", code, [label.serialNumber.toString()].toArray(), 'Error saving label')
						}
						break;
					}
					currentLabelSerial++
				}
				
				if (cmd.errors.hasErrors())
					render(view:'create', model:[cmd: cmd, startSerialNumber: params.startSerialNumber, endSerialNumber: params.endSerialNumber, type:params.type])
				else {				
					flash.message = message(code: 'default.created.message', args: [(params.int('endSerialNumber') - params.int('startSerialNumber') + 1), message(code: 'label.label', default: 'Label')])
					redirect(action: "list", model:params)
				}
		
			}
			else {
				render(view:'create', model:[cmd: cmd, startSerialNumber: params.startSerialNumber, endSerialNumber: params.endSerialNumber, type:params.type])
			}
		}
    }
	
	def assign = { AssignLabelsCommand cmd -> 
		if (request.method == 'POST') {
			if (cmd.validate()) {
				Integer currentLabelSerial = params.int('startSerialNumber'),
						end = params.int('endSerialNumber')
				
				AppUser user = AppUser.get(params.long('user.id'))
				while(currentLabelSerial <= end) {
					def label = Label.findBySerialNumber(currentLabelSerial)
					label.registeredTo = user
					label.assigned = true;
					if (label.validate()) {
						log.debugEnabled ? log.debug('Updating label with id: ' + currentLabelSerial) : ''
						label.save(flush:true)
					}
					else if(!cmd.errors.hasErrors()){
						def labelMessage
						label.errors.allErrors.each {FieldError error ->
							final String field = error.field?.replace('label.', '')
							final String code = "generateLabelsCommand.$field.$error.code"
							log.debug(error)
							cmd.errors.rejectValue("startSerialNumber", code, [label.serialNumber.toString()].toArray(), 'Error saving label')
						}
						break;
					}
					currentLabelSerial++
				}
				
				if (cmd.errors.hasErrors())
					render(view:'assign', model:[cmd: cmd, startSerialNumber: params.startSerialNumber, endSerialNumber: params.endSerialNumber, user:params.user])
				else {
					flash.message = message(code: 'default.updated.message', args: [(params.int('endSerialNumber') - params.int('startSerialNumber') + 1), message(code: 'label.label', default: 'Inventory Labels')])
					redirect(action: "list", model:params)
				}
		
			}
			else {
				render(view:'assign', model:[cmd: cmd, startSerialNumber: params.startSerialNumber, endSerialNumber: params.endSerialNumber, user:params.user])
			}
		}

	}

    def save() {
        def labelInstance = new Label(params)
        if (!labelInstance.save(flush: true)) {
            render(view: "create", model: [labelInstance: labelInstance])
            return
        }

        flash.message = message(code: 'default.created.message', args: [message(code: 'label.label', default: 'Label'), labelInstance.id])
        redirect(action: "show", id: labelInstance.id)
    }

    def show(Long id) {
        def labelInstance = Label.get(id)
        if (!labelInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "list")
            return
        }

        [labelInstance: labelInstance]
    }

    def edit(Long id) {
        def labelInstance = Label.get(id)
        if (!labelInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "list")
            return
        }

        [labelInstance: labelInstance]
    }

    def update(Long id, Long version) {
        def labelInstance = Label.get(id)
        if (!labelInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "list")
            return
        }

        if (version != null) {
            if (labelInstance.version > version) {
                labelInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'label.label', default: 'Label')] as Object[],
                          "Another user has updated this Label while you were editing")
                render(view: "edit", model: [labelInstance: labelInstance])
                return
            }
        }

        labelInstance.properties = params

        if (!labelInstance.save(flush: true)) {
            render(view: "edit", model: [labelInstance: labelInstance])
            return
        }

        flash.message = message(code: 'default.updated.message', args: [message(code: 'label.label', default: 'Label'), labelInstance.id])
        redirect(action: "show", id: labelInstance.id)
    }

    def delete(Long id) {
        def labelInstance = Label.get(id)
        if (!labelInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "list")
            return
        }

        try {
            labelInstance.delete(flush: true)
            flash.message = message(code: 'default.deleted.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "list")
        }
        catch (DataIntegrityViolationException e) {
            flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'label.label', default: 'Label'), id])
            redirect(action: "show", id: id)
        }
    }
}

class GenerateLabelsCommand {
	Long startSerialNumber
	Long endSerialNumber
	LabelType labelType
	
	static constraints = {
		startSerialNumber (blank: false)
		endSerialNumber (blank:false, validator:{val, obj ->
			if (val < obj.startSerialNumber)
	   			return "label.endValue.before.startValue"
				   
			// Duplicate check
			def res = Label.findAllBySerialNumberBetween(obj.startSerialNumber, val)
			if (res?.size() > 0)
				return "label.duplicates.range"
		})
		//labelType
	}
}
